package com.pet.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.pet.api.dto.UserReviewInfoDTO;
import com.pet.common.domain.PageDTO;
import com.pet.common.domain.Result;
import com.pet.common.domain.UserContext;
import com.pet.common.exception.BusinessException;
import com.pet.common.exception.ForbiddenException;
import com.pet.user.domain.dto.LoginFormDTO;
import com.pet.user.domain.dto.RegisterFormDTO;
import com.pet.user.domain.dto.UserInfoFormDTO;
import com.pet.user.domain.po.Address;
import com.pet.user.domain.po.User;
import com.pet.user.domain.query.UserQuery;
import com.pet.user.domain.vo.UserDetailVO;
import com.pet.user.domain.vo.UserInfoVO;
import com.pet.user.domain.vo.UserLoginVO;
import com.pet.user.mapper.UserMapper;
import com.pet.user.service.IAddressService;
import com.pet.user.service.IUserService;
import com.pet.user.utils.JwtUtils;
import com.pet.user.utils.Md5Util;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

import static com.pet.common.utils.RedisConstants.LOGIN_USER_KEY;
import static com.pet.common.utils.RedisConstants.LOGIN_USER_TTL;

/**
 * <p>
 * 用户表 服务实现类
 * </p>
 *
 * @author Cookies
 * @since 2024-06-27
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
    private final IAddressService addressService;
    private final StringRedisTemplate stringRedisTemplate;

    @Override
    public Result login(LoginFormDTO loginFormDTO) {
        String username = loginFormDTO.getUsername();
        User user = lambdaQuery().eq(User::getUsername, username).one();
        if (user == null) {
            return Result.error("该用户不存在");
        }
        String password = loginFormDTO.getPassword();
        if (!Md5Util.checkPassword(password, user.getSalt(), user.getPassword())) {
            return Result.error("账号或密码错误");
        }
        String token = generateAndSaveToken(user);
        UserLoginVO userLoginVO = new UserLoginVO();
        userLoginVO.setToken(token);
        userLoginVO.setUserId(user.getId());
        userLoginVO.setNickname(user.getNickname());
        return Result.success(userLoginVO);
    }

    @Override
    public Result loginByAdmin(LoginFormDTO loginFormDTO) {
        String username = loginFormDTO.getUsername();
        User user = lambdaQuery()
                .eq(User::getUsername, username)
                .one();
        if (user == null) {
            return Result.error("该用户不存在");
        }
        if (user.getRole() != 2) {
            return Result.error("非管理员禁止登录");
        }
        String password = loginFormDTO.getPassword();
        if (!Md5Util.checkPassword(password, user.getSalt(), user.getPassword())) {
            return Result.error("账号或密码错误");
        }
        String token = generateAndSaveToken(user);
        return Result.success(token);
    }

    @Override
    public Result register(RegisterFormDTO registerFormDTO) {
        if (!registerFormDTO.getPassword().equals(registerFormDTO.getConfirmPassword())) {
            return Result.error("两次输入的密码不一致");
        }
        if (lambdaQuery().eq(User::getUsername, registerFormDTO.getUsername()).one() != null) {
            return Result.error("用户名已存在");
        }
        User user = BeanUtil.copyProperties(registerFormDTO, User.class);
        // MD5加盐加密保存
        String salt = UUID.randomUUID().toString();
        user.setPassword(Md5Util.getMD5String(registerFormDTO.getPassword(), salt));
        user.setSalt(salt);
        return save(user) ? Result.success() : Result.error();
    }

    @Override
    public PageDTO<UserDetailVO> getUsersList(UserQuery userQuery) {
        Page<User> page = new Page<>(userQuery.getPageNo(), userQuery.getPageSize());
        lambdaQuery()
                .like(StrUtil.isNotBlank(userQuery.getKeyWord()), User::getNickname, userQuery.getKeyWord())
                .page(page);
        PageDTO<UserDetailVO> pageDTO = PageDTO.of(page, UserDetailVO.class);
        return pageDTO;
    }

    @Override
    public Result getInfoByUser() {
        Integer userId = UserContext.getUser();
        User user = getById(userId);
        if (user == null) {
            return Result.error("用户不存在");
        }
        UserInfoVO userInfoVO = BeanUtil.copyProperties(user, UserInfoVO.class);
        Address address = addressService.lambdaQuery()
                .eq(Address::getUserId, userId)
                .eq(Address::getIsDefault, true)
                .one();
        String defaultAddress = address.getWholeAddress();
        userInfoVO.setDefaultAddress(defaultAddress);
        return Result.success(userInfoVO);
    }

    @Override
    public Result updateInfoByUser(UserInfoFormDTO userInfoFormDTO) {
        // 判断用户是否在操作本人数据
        Integer userId = UserContext.getUser();
        if (userId != userInfoFormDTO.getId()) {
            throw new ForbiddenException("请勿尝试非法操作");
        }
        User user = BeanUtil.copyProperties(userInfoFormDTO, User.class);
        boolean isSuccess = updateById(user);
        return isSuccess ? Result.success() : Result.error();
    }

    @Override
    public void addPointsByPay(Integer totalFee) {
        Integer userId = UserContext.getUser();
        int point = totalFee / 1000;
        boolean isSuccess = lambdaUpdate()
                .setSql("points = points + " + point)
                .eq(User::getId, userId)
                .update();
        if (!isSuccess) {
            log.error("用户id: {}, 添加{}积分失败", userId, point);
            throw new BusinessException("用户积分更新失败");
        }
    }

    @Override
    public Map<Integer, String> getIdNicknameMap(List<Integer> userIds) {
        return lambdaQuery()
                .select(User::getId, User::getNickname)
                .in(User::getId, userIds)
                .list().stream()
                .collect(Collectors.toMap(User::getId, User::getNickname));
    }

    @Override
    public Map<Integer, UserReviewInfoDTO> queryUserReviewInfo(List<Integer> userIds) {
        List<UserReviewInfoDTO> userInfoDTOList = lambdaQuery()
                .select(User::getId, User::getNickname, User::getPhoto)
                .in(User::getId, userIds)
                .list().stream()
                .map(user -> BeanUtil.copyProperties(user, UserReviewInfoDTO.class))
                .collect(Collectors.toList());
        // id与对象的映射
        Map<Integer, UserReviewInfoDTO> userInfoMap = userInfoDTOList.stream()
                .collect(Collectors.toMap(UserReviewInfoDTO::getId, Function.identity()));
        return userInfoMap;
    }

    private String generateAndSaveToken(User user) {
        Map<String, Object> claims = Map.of("userId", user.getId());
        String token = JwtUtils.generateJwt(claims);
        String key = LOGIN_USER_KEY + user.getId();
        stringRedisTemplate.opsForValue().set(key, token, LOGIN_USER_TTL, TimeUnit.HOURS);
        return token;
    }
}
